/*
 * Copyright (c) 2009-2010 HIT Microelectronic Center
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 * Authors: Gou Pengfei
 *
 * Date: Aug. 2010
 *
 */

#ifndef __CPU_BASE_ATOMIC_EDGE_BLOCK_HH__
#define __CPU_BASE_ATOMIC_EDGE_BLOCK_HH__

#include <bitset>
#include <vector>
#include <list>
#include <deque>
#include <queue>

#include "base/statistics.hh"
#include "base/fast_alloc.hh"
#include "base/refcnt.hh"
#include "config/the_isa.hh"
#include "arch/faults.hh"
#include "cpu/edge/comm.hh"

template <class Impl>
class BaseAtomicEdgeBlock : public FastAlloc, public RefCounted
{
  public:
    /** Typedefs from Impl. */
    typedef typename Impl::CPU CPU;
    typedef typename Impl::DynInstPtr DynInstPtr;
    typedef typename Impl::EdgeBlockPtr BlockPtr;

    /** Typedefs from ISA. */
    typedef TheISA::MachInst MachInst;
    typedef TheISA::ExtMachInst ExtMachInst;

    typedef typename std::list<DynInstPtr>::iterator ListIt;
    typedef typename std::list<BlockPtr>::iterator BlockListIt;
    typedef typename std::deque<DynInstPtr>::iterator QueueIt;

    /// Block ID
    typedef TheISA::BlockID BlockID;
    typedef TheISA::InstID InstID;
    typedef TheISA::HeaderInfo HeaderInfo;

    BaseAtomicEdgeBlock(BlockID blockID, CPU* cpu);

    ~BaseAtomicEdgeBlock();

    //std::string name() const;

  protected:

    /** Init some variables. */
    void initVars();

    /** cpu model this block point to*/
    CPU* cpu;

    /** Start addr of this block*/
    Addr startAddr;

    /** Thread ID */
    ThreadID tid;

  public:
    BlockID blockID;
   //bool _speculative;
    uint32_t numTotalInsts;
    uint32_t numNopInsts;
    uint32_t numControlInsts;
    uint32_t numMemRefs;
    uint32_t numStores;
    uint32_t numLoads;
    uint32_t numWrites;
    uint32_t numReads;
    uint32_t numHeadNops;
    uint32_t numConstInsts;
    uint32_t numTestInsts;
    uint32_t numReadWrites;
    uint32_t numDirectControls;
    uint32_t numIndirectControls;
    uint32_t numMoves;
    uint32_t numNullifies;
    uint32_t numPredOnTrues;
    uint32_t numPredOnFalses;

  protected:
    enum Flags {
        InEdgeROB,
        IsSpeculative, // Indicate speculative or not
        IsCompleted,  // Indicate completed or in progress
        IsAcknowledged, // Indicate this block has been acknowledged by commit unit
        IsCommitted,
        IsSquashed,
        IsExecuting,
        IsFault,
        IsNop,
        CanCommit,
        NeedSyscall,
        NumFlags
    };

    std::bitset<NumFlags> flags;

    /** Instruction list of this block. */
    std::list<DynInstPtr> instList;

    BlockListIt blockListIt;

    struct lsidCompare {
        bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const
        {
            return (lhs->staticInst->getLSID()) > (rhs->staticInst->getLSID());
        }
    };

    typedef std::priority_queue<DynInstPtr, std::vector<DynInstPtr>, lsidCompare> MemRefQueue;

    /** The container hold the ld/st insts in lsid order. */
    MemRefQueue memRefInsts;

    /** Fault class indicate the fault caused by this block. */
    Fault fault;

    //#ifndef NDEBUG

    struct destRegCompare {
        bool operator() (const QueueIt &lhs, const QueueIt &rhs) const
        {
            return ((*lhs)->getMappedDestReg(0)) > ((*rhs)->getMappedDestReg(0));
        }
    };

    typedef std::priority_queue<QueueIt, std::vector<QueueIt>, destRegCompare> WriteQueue;

    /** The container hold store insts for dumpping output. */
    WriteQueue writeInsts;

    struct effAddrCompare {
        bool operator() (const QueueIt &lhs, const QueueIt &rhs) const
        {
            //return ((*lhs)->effAddr) > ((*rhs)->effAddr);
            return ((*lhs)->staticInst->getLSID()) > ((*rhs)->staticInst->getLSID());
        }
    };

    typedef std::priority_queue<QueueIt, std::vector<QueueIt>, effAddrCompare> StoreQueue;

    /** The container hold write insts for dumpping output. */
    StoreQueue storeInsts;

    /** @TODO: This should be taken into serious consideration.
      * Add structures for register read/write. 
      */ 
    //DynInstPtr regReads;
    //DynInstPtr regWrites;

    //#endif

  public:
    void setBlockListIt( BlockListIt it ) { blockListIt = it; }
    BlockListIt &getBlockListIt() { return blockListIt; }

    ListIt addInst(DynInstPtr inst_ptr);

    DynInstPtr getInst();
    DynInstPtr getMemRefInst();

    void dumpOutput();

    void setStartPC(Addr pc);
    void setTid ( ThreadID id ) { tid = id; }
    ThreadID getTid() const { return tid;}

    Addr getStartPC();

    uint64_t getNumInst() const;

    uint64_t getNumMemRefInst() const;

    void removeAllInsts();

    void clean();

    BlockID getBlockID() const {return blockID;}

    uint32_t getNumRegWriteInst() const {return numWrites; }

    uint32_t getNumRegReadInst() const {return numReads;}

    uint32_t getNumNopInst() const { return numNopInsts;}

    uint32_t getNumUsefulInst() const { return numTotalInsts - numNopInsts; }

    uint32_t getNumAllInst() const { return numTotalInsts; }

    uint32_t getNumInstButMemRef() const { return (numTotalInsts - numMemRefs); }

    uint32_t getNumStoreInst() const { return numStores; }

    uint32_t getNumLoadInst() const { return numLoads; }

    uint32_t getNumControlInst() const { return numControlInsts;}

    void setSquashed() { flags.set(IsSquashed);}

    void setExecuting() { flags.set(IsExecuting); }

    void setFault(Fault block_fault ) { fault = block_fault; flags.set(IsFault); }

    void setFault() { flags.set(IsFault); }

    void setCompleted() {
        if ( flags[IsExecuting] ) flags.reset(IsExecuting);
        flags.set(IsCompleted);
    }

    void setInEdgeROB() { flags.set(InEdgeROB); }

    void clearInEdgeROB() { flags.reset(InEdgeROB); }

    void setCommitted() { flags.set(IsCommitted); }

    void setCanCommit() { flags.set(CanCommit); }

    void setNeedSyscall() { flags.set(NeedSyscall); }

    void setNop() { flags.set(IsNop); }

    bool isInEdgeROB() const { return flags[InEdgeROB];}

    bool isSpeculative() const  { return flags[IsSpeculative]; }

    bool isNonSpeculative() const { return !flags[IsSpeculative];}

    bool isCompleted()  const { return flags[IsCompleted];}

    bool isAcknowledged()  const {return flags[IsAcknowledged]; }

    bool isSquashed() const { return flags[IsSquashed]; }

    bool isExecuting() const { return flags[IsExecuting]; }

    bool isCommitted() const { return flags[IsCommitted]; }

    bool isFault() const { return flags[IsFault]; }

    bool isNop() const { return flags[IsNop]; }

    Fault getFault() const { return fault; }

    bool readyToCommit() const { return flags[CanCommit]; }

    bool isNeedSyscall() const { return flags[NeedSyscall]; }

    void setSpeculative() {
        flags.set(IsSpeculative);
    }

    void setNonSpeculative() {
        flags.reset(IsSpeculative);
    }
};

#endif //__CPU_BASE_ATOMIC_EDGE_BLOCK_HH__
